import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.ParallelBehaviour;
import jade.core.behaviours.SequentialBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.proto.states.MsgReceiver;


public class CuratorAgent extends Agent{
	
	private LinkedList<Thingy> items = new LinkedList<Thingy>();
	private ArrayList<AID> bidders = new ArrayList<AID>(10);
	private ArrayList<AID> stillBidders = new ArrayList<AID>(10);
	private int currentItem=0;
	private char SEPARATOR='!';
	private int STARTPRICE = 500;
	private boolean bidAccepted;
	private long time;
	private int DEC=50;
	private int DELAY=300;
	
	
	protected void setup(){
		// Register the Curator service in the yellow pages
		DFAgentDescription dfd = new DFAgentDescription();
		dfd.setName(getAID());
		ServiceDescription sd = new ServiceDescription();
		sd.setType("Curator");
		sd.setName("Curator");
		dfd.addServices(sd);
		try {
			DFService.register(this, dfd);
		}
		catch (FIPAException fe) {
			fe.printStackTrace();
		}
		//add items
		items.add(new Thingy("Hard rock","Nature",500));
		items.add(new Thingy("Mona Lisa","Painting",500));
		items.add(new Thingy("Skriiet","Painting",500));
		items.add(new Thingy("Fine picture","Painting",300));
		
		
		ParallelBehaviour pb = new ParallelBehaviour();
		
		//Looping SequentialBehaviour
		SequentialBehaviour seq = new SequentialBehaviour(){
			  public int onEnd() {
				    reset();
				    myAgent.addBehaviour(this);
				    return super.onEnd();
				  }
				};
		
		pb.addSubBehaviour(new Register());
		
		seq.addSubBehaviour(new StartAuction());
		seq.addSubBehaviour(new MakeAgreement(this, DELAY));
		
		
		pb.addSubBehaviour(seq);
		this.addBehaviour(pb);
		
	}
	
	//Wait for bidder to subscribe and add them to list to tell when auction starts
	private class Register extends MsgReceiver{

		@Override
		public void action() {
			MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.SUBSCRIBE);
			ACLMessage msg = myAgent.receive(mt);
			if (msg != null) {
				ACLMessage msga=new ACLMessage(ACLMessage.CONFIRM);
				send(msga);
				bidders.add(msg.getSender());
				System.out.println("Bidder " + msg.getSender().getName() + " subscribing");
			}
			
		}
		
	}
	
	//inform bidders a new auction have started
	private class StartAuction extends OneShotBehaviour{

		@Override
		public void action() {
			bidAccepted=false;
			ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
			
			for(AID a : bidders)
				msg.addReceiver(a);
			
			msg.setContent(items.get(currentItem).name);
			send(msg);
			System.out.println("Bidding starts on "+items.get(currentItem).name);
			stillBidders=bidders;
		}
		
	}
	
	//send CFP andget proposals
	private class MakeAgreement extends TickerBehaviour{
		public MakeAgreement(Agent a, long period) {
			super(a, period);
			// TODO Auto-generated constructor stub
		}

		public void onTick() {
			//make CFP

			items.get(0).price=items.get(0).price-DEC;
			bidAccepted=false;
			ACLMessage CFPmsg = new ACLMessage(ACLMessage.CFP);
			
			for(AID a : stillBidders)
				CFPmsg.addReceiver(a);
			try {
				CFPmsg.setContentObject(items.get(0));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			send(CFPmsg);
			System.out.println("Bid on "+items.get(currentItem).name+" now just "+items.get(0).price);
			
			//Get bids
			MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.PROPOSE);

			ACLMessage inMsg;
			ACLMessage rMsg = new ACLMessage(ACLMessage.REJECT_PROPOSAL);
			ACLMessage aMsg = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
			ACLMessage iMsg = new ACLMessage(ACLMessage.INFORM);


			time = System.currentTimeMillis();
			while (System.currentTimeMillis() < time + DELAY-(DELAY/10)) {
				inMsg = myAgent.receive(mt);
				if (inMsg != null) {
					
					
					
					if (inMsg.getPerformative() == ACLMessage.PROPOSE && bidAccepted == false) {
						bidAccepted = true;
						aMsg.addReceiver(inMsg.getSender());
						System.out.println(inMsg.getSender().getLocalName()
								+ " won the auction");
						send(aMsg);
					} else if (inMsg.getPerformative() == ACLMessage.NOT_UNDERSTOOD) {
						stillBidders.remove(inMsg.getSender());
						System.out.println(inMsg.getSender() + " is out");
					} else if (inMsg.getPerformative() == ACLMessage.PROPOSE &&bidAccepted == true) {
						rMsg.addReceiver(inMsg.getSender());
						System.out.println(inMsg.getSender().getLocalName() + " is too late");
						send(rMsg);
					} else {
						System.out.println(inMsg.getSender() + " is waiting");
					}
					inMsg=null;
	
				}
				
			}
			//if no more bidders or price will reach 0 or below next round, stop auction
			if (stillBidders.isEmpty() || items.get(0).price <= DEC) {
				//System.out.println(bidAccepted+""+stillBidders.isEmpty()+items.get(0).price);
				System.out.println("Noone bought item");
				iMsg.setContent("Auctioon over");
				send(iMsg);
				items.add(items.get(0));
				items.remove();
				items.get(0).price=STARTPRICE;
				this.stop();
			}
			//if someone won
			if(bidAccepted){
				System.out.println("Someone won item");
				items.remove(currentItem);
				items.get(0).price=STARTPRICE;
				bidAccepted = false;
				this.stop();
			}
		}
	}
}
